#pragma warning(disable:4200)  
#pragma warning(disable:4201)  
#pragma warning(disable:4214)  
    
#include <initguid.h>
#include <wdm.h>
#include "usbdi.h"
#include "usbdlib.h"

#pragma warning(default:4200)
#pragma warning(default:4201)
#pragma warning(default:4214)

#include <wdf.h>
#include <wdfusb.h>

#include "ringBuf.h"
#include "public.h"
#include "prototypes.h"
#include "ioctl.h"

NTSTATUS rb_init(IN WDFDEVICE device)
{
    NTSTATUS ntStatus;
    RING_BUF *p_rb;
    WDF_OBJECT_ATTRIBUTES   attributes;
    PDEVICE_CONTEXT p_deviceContext;
    
    p_deviceContext = GetDeviceContext(device);
    p_rb = &(p_deviceContext->rb);
    
    // ring buff lock init
    WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
    attributes.ParentObject = device;
    
    ntStatus = WdfSpinLockCreate (&attributes, &(p_rb->lock));
    if (!NT_SUCCESS(ntStatus))
    {
        KdPrint(("[XIU9011] rb_init: WdfWaitLockCreate Err \n"));
        goto    RB_INIT_EXIT;
	}
	
	// ring buff memory allocation
    WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
    attributes.ParentObject = device;
    
    ntStatus = WdfMemoryCreate(
                         &attributes,
                         NonPagedPool,
                         (ULONG)POOL_TAG,
                         RING_BUF_SIZE,
                         &(p_rb->wdfmem_obj),
                         NULL
                         );
    if (!NT_SUCCESS(ntStatus)) {
        KdPrint(("[XIU9011] rb_init: WdfMemoryCreate Err \n"));
        goto    RB_INIT_EXIT;
    }
    
    p_rb->p_buf = (UCHAR *)WdfMemoryGetBuffer(p_rb->wdfmem_obj, NULL);
    p_rb->size = RING_BUF_SIZE;
	
	// write & read index initialize
    p_rb->wr_idx = 0;
    p_rb->rd_idx = 0;
  
RB_INIT_EXIT:
    
    return ntStatus;    
}

size_t rb_write(RING_BUF *p_rb, UCHAR *buf, size_t len)
{
    size_t freeDataCnt;
    size_t i;

    // Acquire ring_buf lock
    WdfSpinLockAcquire(p_rb->lock);

    freeDataCnt = rb_getFreeDataSize(p_rb);

    if(len > freeDataCnt) {
        len = freeDataCnt;
    }
    else {
        freeDataCnt = len;
    }

    i = p_rb->wr_idx;

    if(i + len > p_rb->size) {
        RtlCopyMemory((PVOID)(p_rb->p_buf + i), buf, p_rb->size - i);
        buf += p_rb->size - i;
        len -= p_rb->size - i;
        i = 0;
    }
    
    RtlCopyMemory((PVOID)(p_rb->p_buf + i), buf, len);
    p_rb->wr_idx = i + len;

    // Release ring_buf lock
    WdfSpinLockRelease(p_rb->lock);

    return freeDataCnt;
}

size_t rb_read(RING_BUF *p_rb, UCHAR *buf, size_t max_len)
{
    size_t availDataCnt;
    size_t i;
    
    // Acquire ring_buf lock
    WdfSpinLockAcquire(p_rb->lock);
    
    availDataCnt = rb_getAvailableDataSize(p_rb);

    if(max_len > availDataCnt) {
        max_len = availDataCnt;
    }
    else {
        availDataCnt = max_len;
    }
            

    i = p_rb->rd_idx;
    

    if(i + max_len > p_rb->size) {
        RtlCopyMemory(buf, (PVOID)(p_rb->p_buf + i), p_rb->size - i);
        buf += p_rb->size - i;
        max_len -= p_rb->size - i;
        i = 0;
    }
   
    RtlCopyMemory(buf, (PVOID)(p_rb->p_buf + i), max_len);
    p_rb->rd_idx = i + max_len;
    
    // Relaase ring_buf lock
    WdfSpinLockRelease(p_rb->lock);

    return availDataCnt;
}

size_t rb_getAvailableDataSize(RING_BUF *p_rb)
{
    size_t size;
    
    size = ((p_rb->wr_idx - p_rb->rd_idx) & (p_rb->size - 1));
    
    return size;    
}

size_t rb_getFreeDataSize(RING_BUF *p_rb)
{
    size_t size;
    
    size = p_rb->size - 1 - rb_getAvailableDataSize(p_rb);
    
    return size;
}

void rb_clear(RING_BUF *p_rb)
{
    // Acquire ring_buf lock
    WdfSpinLockAcquire(p_rb->lock);
    
    p_rb->wr_idx = 0;
    p_rb->rd_idx = 0;
    
    // Release ring_buf lock
    WdfSpinLockRelease(p_rb->lock);
}